'use strict';
/*
* 小型游戏引擎
*/

// requestAnimationFrame polyfill
if (!Date.now)
    Date.now = function () { return new Date().getTime(); };
(function () {
    'use strict';
    var vendors = ['webkit', 'moz'];
    for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) {
        var vp = vendors[i];
        window.requestAnimationFrame = window[vp + 'RequestAnimationFrame'];
        window.cancelAnimationFrame = (window[vp + 'CancelAnimationFrame'] || window[vp + 'CancelRequestAnimationFrame']);
    }
    if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy
        || !window.requestAnimationFrame || !window.cancelAnimationFrame) {
        var lastTime = 0;
        window.requestAnimationFrame = function (callback) {
            var now = Date.now();
            var nextTime = Math.max(lastTime + 16, now);
            return setTimeout(function () { callback(lastTime = nextTime); },
                nextTime - now);
        };
        window.cancelAnimationFrame = clearTimeout;
    }
}());

function Game(id, params) {
    var _ = this;
    var settings = {
        width: 960,						//画布宽度
        height: 640						//画布高度
    };
    var _extend = function (target, settings, params) {
        params = params || {};
        for (var i in settings) {
            target[i] = params[i] || settings[i];
        }
        return target;
    };
    _extend(_, settings, params);
    var $canvas = document.getElementById(id);
    $canvas.width = _.width;
    $canvas.height = _.height;
    var _context = $canvas.getContext('2d');	//画布上下文环境
    var _stages = [];							//布景对象队列
    var _events = {};							//事件集合
    var _index = 0,								//当前布景索引
        _hander;  								//帧动画控制
    //活动对象构造
    var Item = function (params) {
        this._params = params || {};
        this._id = 0;               //标志符
        this._stage = null;         //与所属布景绑定
        this._settings = {
            x: 0,					//位置坐标:横坐标
            y: 0,					//位置坐标:纵坐标
            width: 20,				//宽
            height: 20,				//高
            type: 0,					//对象类型,0表示普通对象(不与地图绑定),1表示玩家控制对象,2表示程序控制对象
            color: '#F00',			//标识颜色
            status: 1,				//对象状态,0表示未激活/结束,1表示正常,2表示暂停,3表示临时,4表示异常
            orientation: 0,			//当前定位方向,0表示右,1表示下,2表示左,3表示上
            speed: 0,				//移动速度
            //地图相关
            location: null,			//定位地图,Map对象
            coord: null,				//如果对象与地图绑定,需设置地图坐标;若不绑定,则设置位置坐标
            path: [],				//NPC自动行走的路径
            vector: null,			//目标坐标
            //布局相关
            frames: 1,				//速度等级,内部计算器times多少帧变化一次
            times: 0,				//刷新画布计数(用于循环动画状态判断)
            timeout: 0,				//倒计时(用于过程动画状态判断)
            control: {},				//控制缓存,到达定位点时处理
            update: function () { }, 	//更新参数信息
            draw: function () { }		//绘制
        };
        _extend(this, this._settings, this._params);
    };
    Item.prototype.bind = function (eventType, callback) {
        if (!_events[eventType]) {
            _events[eventType] = {};
            $canvas.addEventListener(eventType, function (e) {
                var position = _.getPosition(e);
                _stages[_index].items.forEach(function (item) {
                    if (Math.abs(position.x - item.x) < item.width / 2 && Math.abs(position.y - item.y) < item.height / 2) {
                        var key = 's' + _index + 'i' + item._id;
                        if (_events[eventType][key]) {
                            _events[eventType][key](e);
                        }
                    }
                });
                e.preventDefault();
            });
        }
        _events[eventType]['s' + this._stage.index + 'i' + this._id] = callback.bind(this);  //绑定作用域
    };
    //地图对象构造器
    var Map = function (params) {
        this._params = params || {};
        this._id = 0;               //标志符
        this._stage = null;         //与所属布景绑定
        this._settings = {
            x: 0,					//地图起点坐标
            y: 0,
            size: 20,				//地图单元的宽度
            data: [],				//地图数据
            x_length: 0,				//二维数组x轴长度
            y_length: 0,				//二维数组y轴长度
            frames: 1,				//速度等级,内部计算器times多少帧变化一次
            times: 0,				//刷新画布计数(用于循环动画状态判断)
            cache: false,    		//是否静态（如静态则设置缓存）
            update: function () { },	//更新地图数据
            draw: function () { },		//绘制地图
        };
        _extend(this, this._settings, this._params);
    };
    //获取地图上某点的值
    Map.prototype.get = function (x, y) {
        if (this.data[y] && typeof this.data[y][x] != 'undefined') {
            return this.data[y][x];
        }
        return -1;
    };
    //设置地图上某点的值
    Map.prototype.set = function (x, y, value) {
        if (this.data[y]) {
            this.data[y][x] = value;
        }
    };
    //地图坐标转画布坐标
    Map.prototype.coord2position = function (cx, cy) {
        return {
            x: this.x + cx * this.size + this.size / 2,
            y: this.y + cy * this.size + this.size / 2
        };
    };
    //画布坐标转地图坐标
    Map.prototype.position2coord = function (x, y) {
        var fx = Math.abs(x - this.x) % this.size - this.size / 2;
        var fy = Math.abs(y - this.y) % this.size - this.size / 2;
        return {
            x: Math.floor((x - this.x) / this.size),
            y: Math.floor((y - this.y) / this.size),
            offset: Math.sqrt(fx * fx + fy * fy)
        };
    };
    //寻址算法
    Map.prototype.finder = function (params) {
        var defaults = {
            map: null,
            start: {},
            end: {},
            type: 'path'
        };
        var options = _extend({}, defaults, params);
        if (options.map[options.start.y][options.start.x] || options.map[options.end.y][options.end.x]) { //当起点或终点设置在墙上
            return [];
        }
        var finded = false;
        var result = [];
        var y_length = options.map.length;
        var x_length = options.map[0].length;
        var steps = [];     //步骤的映射
        for (var y = y_length; y--;) {
            steps[y] = new Array(x_length).fill(0);
        }
        var _getValue = function (x, y) {  //获取地图上的值
            if (options.map[y] && typeof options.map[y][x] != 'undefined') {
                return options.map[y][x];
            }
            return -1;
        };
        var _next = function (to) { //判定是否可走,可走放入列表
            var value = _getValue(to.x, to.y);
            if (value < 1) {
                if (value == -1) {
                    to.x = (to.x + x_length) % x_length;
                    to.y = (to.y + y_length) % y_length;
                    to.change = 1;
                }
                if (!steps[to.y][to.x]) {
                    result.push(to);
                }
            }
        };
        var _render = function (list) {//找线路
            var new_list = [];
            var next = function (from, to) {
                var value = _getValue(to.x, to.y);
                if (value < 1) {	//当前点是否可以走
                    if (value == -1) {
                        to.x = (to.x + x_length) % x_length;
                        to.y = (to.y + y_length) % y_length;
                        to.change = 1;
                    }
                    if (to.x == options.end.x && to.y == options.end.y) {
                        steps[to.y][to.x] = from;
                        finded = true;
                    } else if (!steps[to.y][to.x]) {
                        steps[to.y][to.x] = from;
                        new_list.push(to);
                    }
                }
            };
            list.forEach(function (current) {
                next(current, { y: current.y + 1, x: current.x });
                next(current, { y: current.y, x: current.x + 1 });
                next(current, { y: current.y - 1, x: current.x });
                next(current, { y: current.y, x: current.x - 1 });
            });
            if (!finded && new_list.length) {
                _render(new_list);
            }
        };
        _render([options.start]);
        if (finded) {
            var current = options.end;
            if (options.type == 'path') {
                while (current.x != options.start.x || current.y != options.start.y) {
                    result.unshift(current);
                    current = steps[current.y][current.x];
                }
            } else if (options.type == 'next') {
                _next({ x: current.x + 1, y: current.y });
                _next({ x: current.x, y: current.y + 1 });
                _next({ x: current.x - 1, y: current.y });
                _next({ x: current.x, y: current.y - 1 });
            }
        }
        return result;
    };
    //布景对象构造器
    var Stage = function (params) {
        this._params = params || {};
        this._settings = {
            index: 0,                        //布景索引
            status: 0,						//布景状态,0表示未激活/结束,1表示正常,2表示暂停,3表示临时,4表示异常
            maps: [],						//地图队列
            audio: [],						//音频资源
            images: [],						//图片资源
            items: [],						//对象队列
            timeout: 0,						//倒计时(用于过程动画状态判断)
            update: function () { }				//嗅探,处理布局下不同对象的相对关系
        };
        _extend(this, this._settings, this._params);
    };
    //添加对象
    Stage.prototype.createItem = function (options) {
        var item = new Item(options);
        //动态属性
        if (item.location) {
            var position = item.location.coord2position(item.coord.x, item.coord.y);
            item.x = position.x;
            item.y = position.y;
        }
        //关系绑定
        item._stage = this;
        item._id = this.items.length;
        this.items.push(item);
        return item;
    };
    //重置物体位置
    Stage.prototype.resetItems = function () {
        this.status = 1;
        this.items.forEach(function (item, index) {
            _extend(item, item._settings, item._params);
            if (item.location) {
                var position = item.location.coord2position(item.coord.x, item.coord.y);
                item.x = position.x;
                item.y = position.y;
            }
        });
    };
    //获取对象列表
    Stage.prototype.getItemsByType = function (type) {
        return this.items.filter(function (item) {
            if (item.type == type) {
                return item;
            }
        });
    };
    //添加地图
    Stage.prototype.createMap = function (options) {
        var map = new Map(options);
        //动态属性
        map.data = JSON.parse(JSON.stringify(map._params.data));
        map.y_length = map.data.length;
        map.x_length = map.data[0].length;
        map.imageData = null;
        //关系绑定
        map._stage = this;
        map._id = this.maps.length;
        this.maps.push(map);
        return map;
    };
    //重置地图
    Stage.prototype.resetMaps = function () {
        this.status = 1;
        this.maps.forEach(function (map) {
            _extend(map, map._settings, map._params);
            map.data = JSON.parse(JSON.stringify(map._params.data));
            map.y_length = map.data.length;
            map.x_length = map.data[0].length;
            map.imageData = null;
        });
    };
    //重置
    Stage.prototype.reset = function () {
        _extend(this, this._settings, this._params);
        this.resetItems();
        this.resetMaps();
    };
    //绑定事件
    Stage.prototype.bind = function (eventType, callback) {
        if (!_events[eventType]) {
            _events[eventType] = {};
            window.addEventListener(eventType, function (e) {
                var key = 's' + _index;
                if (_events[eventType][key]) {
                    _events[eventType][key](e);
                }
                e.preventDefault();
            });
        }
        _events[eventType]['s' + this.index] = callback.bind(this);	//绑定事件作用域
    };
    //动画开始
    this.start = function () {
        var f = 0;		//帧数计算
        var fn = function () {
            var stage = _stages[_index];
            _context.clearRect(0, 0, _.width, _.height);		//清除画布
            _context.fillStyle = '#000000';
            _context.fillRect(0, 0, _.width, _.height);
            f++;
            if (stage.timeout) {
                stage.timeout--;
            }
            if (stage.update() != false) {		            //update返回false,则不绘制
                stage.maps.forEach(function (map) {
                    if (!(f % map.frames)) {
                        map.times = f / map.frames;		//计数器
                    }
                    if (map.cache) {
                        if (!map.imageData) {
                            _context.save();
                            map.draw(_context);
                            map.imageData = _context.getImageData(0, 0, _.width, _.height);
                            _context.restore();
                        } else {
                            _context.putImageData(map.imageData, 0, 0);
                        }
                    } else {
                        map.update();
                        map.draw(_context);
                    }
                });
                stage.items.forEach(function (item) {
                    if (!(f % item.frames)) {
                        item.times = f / item.frames;		   //计数器
                    }
                    if (stage.status == 1 && item.status != 2) {  	//对象及布景状态都不处于暂停状态
                        if (item.location) {
                            item.coord = item.location.position2coord(item.x, item.y);
                        }
                        if (item.timeout) {
                            item.timeout--;
                        }
                        item.update();
                    }
                    item.draw(_context);
                });
            }
            _hander = requestAnimationFrame(fn);
        };
        _hander = requestAnimationFrame(fn);
    };
    //动画结束
    this.stop = function () {
        _hander && cancelAnimationFrame(_hander);
    };
    //事件坐标
    this.getPosition = function (e) {
        var box = $canvas.getBoundingClientRect();
        return {
            x: e.clientX - box.left * (_.width / box.width),
            y: e.clientY - box.top * (_.height / box.height)
        };
    }
    //创建布景
    this.createStage = function (options) {
        var stage = new Stage(options);
        stage.index = _stages.length;
        _stages.push(stage);
        return stage;
    };
    //指定布景
    this.setStage = function (index) {
        _stages[_index].status = 0;
        _index = index;
        _stages[_index].status = 1;
        return _stages[_index];
    };
    //下个布景
    this.nextStage = function () {
        if (_index < _stages.length - 1) {
            return this.setStage(++_index);
        } else {
            throw new Error('unfound new stage.');
        }
    };
    //初始化游戏引擎
    this.init = function () {
        _index = 0;
        this.start();
    };
}
